#include "friend_server.h"

#include "data_es.h"
#include "log.h"
#include "message.pb.h"
#include "user.pb.h"
#include "uuid.h"

namespace chat {
// FriendServerBuilder
void FriendServiceImpl::GetFriendList(
    ::google::protobuf::RpcController* controller,
    const ::chat::GetFriendListReq* request, ::chat::GetFriendListRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到好友列表查询请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";

    do {
        // 1. 获取请求中的用户ID
        const std::string& user_id = request->user_id();

        // 2. 根据用户ID，从数据库的好友关系表&用户表中取出该用户所有的好友简息
        std::vector<std::string> friend_ids =
            odb_relationship_->friends(user_id);
        std::unordered_set<std::string> friend_id_set(friend_ids.begin(),
                                                      friend_ids.end());

        std::unordered_map<std::string, chat::UserInfo> friend_infos;
        if (!getUsersInfo(friend_id_set, &friend_infos)) {
            LOG_ERROR("{}: 获取好友信息失败", request_id);

            is_success = false;
            err_msg = "获取好友信息失败";

            break;
        }

        // 3. 将获取到的好友信息填充到响应
        for (auto& user_info : friend_infos) {
            response->add_friend_list()->CopyFrom(user_info.second);
        }
    } while (0);

    // 4. 组织响应，将好友列表返回给网关。
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);

    LOG_DEBUG("成功处理好友列表查询请求: {}", request_id);
}

void FriendServiceImpl::FriendRemove(
    ::google::protobuf::RpcController* controller,
    const ::chat::FriendRemoveReq* request, ::chat::FriendRemoveRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到好友删除请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";

    do {
        // 1. 取出请求中的请求者ID，和被请求者ID
        const std::string& user_id = request->user_id();
        const std::string& peer_id = request->peer_id();

        // 2. 从用户好友关系表中删除相关关系数据
        // 从会话表中删除单聊会话,从会话成员表中删除会话成员信息
        if (!odb_relationship_->remove(user_id, peer_id)) {
            LOG_ERROR("{}: 好友删除失败[{}-{}]", request_id, user_id, peer_id);

            is_success = false;
            err_msg = "好友删除失败";

            break;
        }
        if (!odb_session_info_->remove(user_id, peer_id)) {
            LOG_ERROR("{}: 好友单聊会话删除失败[{}-{}]", request_id, user_id,
                      peer_id);

            is_success = false;
            err_msg = "好友单聊会话删除失败";

            break;
        }

    } while (0);

    // 5. 组织响应
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);

    LOG_DEBUG("成功处理好友删除请求: {}", request_id);
}

void FriendServiceImpl::FriendAdd(::google::protobuf::RpcController* controller,
                                  const ::chat::FriendAddReq* request,
                                  ::chat::FriendAddRsp* response,
                                  ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到 发送好友添加 请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";
    std::string event_id;

    do {
        // 1. 取出请求中的用户ID
        const std::string& user_id = request->user_id();        // 申请人ID
        const std::string& peer_id = request->respondent_id();  // 被申请人ID
        if (odb_friend_request_->exists(user_id, peer_id)) {
            LOG_ERROR("{}: 已经发送过好友添加请求[{}->{}]", request_id, user_id,
                      peer_id);

            is_success = false;
            err_msg = "已经发送过好友添加请求";

            break;
        }

        event_id = Uuid::uuid();  // 事件ID

        // 2. 插入好友添加请求事件
        FriendRequest friend_request(event_id, user_id, peer_id);
        if (!odb_friend_request_->insert(friend_request)) {
            LOG_ERROR("{}: 好友添加请求事件插入失败[{}->{}]", request_id,
                      user_id, peer_id);

            is_success = false;
            err_msg = "好友添加请求事件插入失败";

            break;
        }
    } while (0);

    // 4. 组织响应，将申请事件列表响应给网关
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);
    response->set_notify_event_id(event_id);

    LOG_DEBUG("成功处理 发送好友添加 请求: {}", request_id);
}

void FriendServiceImpl::FriendAddProcess(
    ::google::protobuf::RpcController* controller,
    const ::chat::FriendAddProcessReq* request,
    ::chat::FriendAddProcessRsp* response, ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到 处理好友添加 请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";
    std::string chat_session_id;

    do {
        // 1. 取出请求中的申请人ID，和被申请人ID，以及处理结果
        const std::string& event_id = request->notify_event_id();  // 事件ID
        const std::string& user_id = request->apply_user_id();     // 申请人ID
        const std::string& peer_id = request->user_id();           // 被申请人ID
        bool agree = request->agree();

        // 2. 根据两人ID在申请事件表中查询判断是否存在申请事件
        if (!odb_friend_request_->exists(user_id, peer_id)) {
            LOG_ERROR("{}: 好友添加请求事件不存在[{}->{}]", request_id, user_id,
                      peer_id);

            is_success = false;
            err_msg = "好友添加请求事件不存在";

            break;
        }

        // 3. 判断两人是否已经是好友（互相加好友的情况）
        if (odb_relationship_->exist(user_id, peer_id)) {
            LOG_WARN("{}: 双方已经是好友[{}-{}]", request_id, user_id,
                      peer_id);

            break;
        }

        // 4. 不管拒绝还是同意，删除申请事件表中的事件信息（该事件处理完毕）
        if (!odb_friend_request_->remove(user_id, peer_id)) {
            LOG_ERROR("{}: 好友添加请求事件删除失败[{}->{}]", request_id,
                      user_id, peer_id);

            is_success = false;
            err_msg = "好友添加请求事件删除失败";

            break;
        }

        // 5. 若同意申请，则向用户关系表中添加好友关系数据
        // 向会话表中新增会话信息，向会话成员表中新增成员信息
        if (agree) {
            // 5.1 向用户关系表中添加好友关系数据
            Relationship relationship(user_id, peer_id);
            if (!odb_relationship_->insert(user_id, peer_id)) {
                LOG_ERROR("{}: 好友关系数据插入失败[{}-{}]", request_id,
                          user_id, peer_id);

                is_success = false;
                err_msg = "好友关系数据插入失败";

                break;
            }

            // 5.2 向会话表中新增会话信息
            chat_session_id = Uuid::uuid();
            ChatSessionInfomation session_info(
                chat_session_id, "", (unsigned char)ChatSessionType::SINGLE);
            if (!odb_session_info_->insert(session_info)) {
                LOG_ERROR("{}: 好友单聊会话插入失败[{}-{}]", request_id,
                          user_id, peer_id);

                is_success = false;
                err_msg = "好友单聊会话插入失败";

                break;
            }

            // 5.3 向会话成员表添加会话成员信息
            ChatSessionMember member1(chat_session_id, user_id);
            ChatSessionMember member2(chat_session_id, peer_id);
            std::vector<ChatSessionMember> members = {member1, member2};
            if (!odb_session_member_->append(members)) {
                LOG_ERROR("{}: 好友单聊会话成员插入失败[{}-{}]", request_id,
                          user_id, peer_id);

                is_success = false;
                err_msg = "好友单聊会话成员插入失败";

                break;
            }
        }

    } while (0);

    // 6. 组织响应，将新生成的会话ID响应给网关。
    response->set_new_session_id(chat_session_id);
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);
}

void FriendServiceImpl::FriendSearch(
    ::google::protobuf::RpcController* controller,
    const ::chat::FriendSearchReq* request, ::chat::FriendSearchRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到 陌生人搜索 请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";

    do {
        // 1. 取出请求中的用户ID，和搜索关键字
        const std::string& user_id = request->user_id();
        const std::string& keyword = request->search_key();

        // 2. 从好友关系表中取出该用户所有好友ID
        std::vector<std::string> friend_ids =
            odb_relationship_->friends(user_id);
        friend_ids.push_back(user_id);

        // 3.根据关键字从ES服务器中进行用户搜索，搜索的时候需要过滤掉自己和自己的好友
        std::vector<User> user_infos = es_user_->search(keyword, friend_ids);
        std::unordered_set<std::string> stranger_ids;
        for (auto& user_info : user_infos) {
            stranger_ids.insert(user_info.user_id());
        }

        // 4. 根据搜索到的用户ID，从用户服务器批量获取用户信息
        std::unordered_map<std::string, chat::UserInfo> stranger_infos;
        if (!getUsersInfo(stranger_ids, &stranger_infos)) {
            LOG_ERROR("{}: 获取陌生人信息失败", request_id);

            is_success = false;
            err_msg = "获取陌生人信息失败";

            break;
        }

        for (auto& user_info : stranger_infos) {
            response->add_user_info()->CopyFrom(user_info.second);
        }

    } while (0);

    // 5. 组织响应，将搜索到的用户列表响应给网关
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);

    LOG_DEBUG("{}: 陌生人搜索响应成功", request_id);
}

void FriendServiceImpl::GetPendingFriendEventList(
    ::google::protobuf::RpcController* controller,
    const ::chat::GetPendingFriendEventListReq* request,
    ::chat::GetPendingFriendEventListRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到 获取待处理好友事件列表 请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";

    do {
        // 1. 获取被申请人的用户ID
        const std::string& user_id = request->user_id();

        // 2. 查询哪些人对 user_id 发送了好友申请
        std::vector<std::string> peer_ids = odb_friend_request_->peers(user_id);
        std::unordered_set<std::string> peer_id_set(peer_ids.begin(),
                                                    peer_ids.end());

        // 3. 从用户服务器批量获取用户信息
        std::unordered_map<std::string, chat::UserInfo> peer_infos;
        if (!getUsersInfo(peer_id_set, &peer_infos)) {
            LOG_ERROR("{}: 获取 申请用户信息 失败", request_id);

            is_success = false;
            err_msg = "获取 申请用户信息 失败";

            break;
        }

        // 4. 组织响应
        for (auto& user_info : peer_infos) {
            auto event_info = response->add_event();
            event_info->mutable_sender()->CopyFrom(user_info.second);
        }
    } while (0);

    // 5. 组织响应，将搜索到的用户列表响应给网关
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);

    LOG_DEBUG("{}: 获取待处理好友事件列表响应成功", request_id);
}

void FriendServiceImpl::GetChatSessionList(
    ::google::protobuf::RpcController* controller,
    const ::chat::GetChatSessionListReq* request,
    ::chat::GetChatSessionListRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到 获取好友会话列表 请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";

    do {
        // 1. 取出用户ID
        const std::string& user_id = request->user_id();

        // 2. 获取用户的单聊会话信息
        std::vector<SingleSessionInfo> single_session_infos =
            odb_session_info_->SingleSessionInfos(user_id);

        // 2.1 获取对端的用户ID
        std::unordered_set<std::string> peer_id_set;
        for (auto& single_session_info : single_session_infos) {
            peer_id_set.insert(single_session_info.peer_id);
        }

        // 2.2 获取对端的用户信息
        std::unordered_map<std::string, chat::UserInfo> peer_infos;
        if (!getUsersInfo(peer_id_set, &peer_infos)) {
            LOG_ERROR("{}: 获取 对端用户信息 失败", request_id);

            is_success = false;
            err_msg = "获取 对端用户信息 失败";

            break;
        }

        // 2.3 组织响应
        for (auto& single_session_info : single_session_infos) {
            auto rsp_chat_session_info = response->add_chat_session_info_list();

            rsp_chat_session_info->set_single_chat_friend_id(
                single_session_info.peer_id);
            rsp_chat_session_info->set_chat_session_id(
                single_session_info.chat_session_id);
            rsp_chat_session_info->set_chat_session_name(
                peer_infos[single_session_info.peer_id].nickname());
            rsp_chat_session_info->set_avatar(
                peer_infos[single_session_info.peer_id].avatar());

            // 获取最近一条消息
            MessageInfo message_info;
            if (getRecentOneMessage(single_session_info.chat_session_id,
                                    &message_info)) {
                rsp_chat_session_info->mutable_prev_message()->CopyFrom(
                    message_info);
            }
        }

        // 3. 获取用户的群聊会话信息
        std::vector<GroupSessionInfo> group_session_infos =
            odb_session_info_->groupSessionInfos(user_id);

        // 3.1 组织响应
        for (auto& group_session_info : group_session_infos) {
            const std::string& chat_session_id =
                group_session_info.chat_session_id;

            auto rsp_chat_session_info = response->add_chat_session_info_list();

            rsp_chat_session_info->set_chat_session_id(chat_session_id);
            rsp_chat_session_info->set_chat_session_name(
                group_session_info.chat_session_name);

            // 获取最近一条消息
            MessageInfo message_info;
            if (getRecentOneMessage(chat_session_id, &message_info)) {
                rsp_chat_session_info->mutable_prev_message()->CopyFrom(
                    message_info);
            }
        }

    } while (0);

    // 4. 组织响应
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);

    LOG_DEBUG("{}: 获取好友会话列表响应成功", request_id);
}

void FriendServiceImpl::ChatSessionCreate(
    ::google::protobuf::RpcController* controller,
    const ::chat::ChatSessionCreateReq* request,
    ::chat::ChatSessionCreateRsp* response, ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到 获取好友会话列表 请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";

    do {
        // 1. 获取关键信息，并创建会话ID
        const std::string& chat_session_name = request->chat_session_name();
        auto member_ids = request->member_id_list();
        const std::string& chat_session_id = Uuid::uuid();

        // 2. 创建会话
        ChatSessionInfomation chat_session_info(
            chat_session_id, chat_session_name,
            (unsigned char)ChatSessionType::GROUP);
        if (!odb_session_info_->insert(chat_session_info)) {
            LOG_ERROR("{}: [{}]会话信息插入失败", request->request_id(),
                      chat_session_id);

            is_success = false;
            err_msg = "会话信息插入失败";

            break;
        }

        // 3. 向会话成员表中插入会话成员
        std::vector<ChatSessionMember> session_members;
        for (auto& member_id : member_ids) {
            session_members.emplace_back(chat_session_id, member_id);
        }

        if (!odb_session_member_->append(session_members)) {
            LOG_ERROR("{}: [{}]会话成员信息插入失败", request_id,
                      chat_session_id);

            is_success = false;
            err_msg = "会话成员信息插入失败";

            break;
        }
        // 3. 组织响应
        response->mutable_chat_session_info()->set_chat_session_name(
            chat_session_name);
        response->mutable_chat_session_info()->set_chat_session_id(
            chat_session_id);

    } while (0);

    // 4. 组织响应
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);

    LOG_DEBUG("{}: 成功处理 创建会话请求", request_id);
}

void FriendServiceImpl::GetChatSessionMember(
    ::google::protobuf::RpcController* controller,
    const ::chat::GetChatSessionMemberReq* request,
    ::chat::GetChatSessionMemberRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("收到 获取会话成员列表 请求: {}", request->request_id());

    std::string request_id = request->request_id();
    bool is_success = true;
    std::string err_msg = "OK";

    do {
        // 1. 获取会话ID
        const std::string& chat_session_id = request->chat_session_id();

        // 2. 获取会话成员id
        std::vector<std::string> member_ids =
            odb_session_member_->members(chat_session_id);
        std::unordered_set<std::string> member_id_set;
        for (auto& member_id : member_ids) {
            member_id_set.insert(member_id);
        }

        // 3. 获取会话成员信息
        std::unordered_map<std::string, chat::UserInfo> member_infos;
        if (!getUsersInfo(member_id_set, &member_infos)) {
            LOG_ERROR("{}: 获取 会话成员信息 失败", request_id);

            is_success = false;
            err_msg = "获取 会话成员信息 失败";

            break;
        }

        // 2.1 组织响应
        for (auto& member_info : member_infos) {
            response->add_member_info_list()->CopyFrom(member_info.second);
        }

    } while (0);

    // 4. 组织响应
    response->set_request_id(request_id);
    response->set_success(is_success);
    response->set_errmsg(err_msg);

    LOG_DEBUG("{}: 成功处理 获取会话成员列表 请求", request_id);
}

bool FriendServiceImpl::getUsersInfo(
    const std::unordered_set<std::string>& user_ids,
    std::unordered_map<std::string, chat::UserInfo>* user_infos) {
    chat::ServiceChannel::ChannelPtr channel =
        service_manager_->choose(user_service_name_);
    if (nullptr == channel) {
        LOG_ERROR("未找到能够提供服务 {} 的节点", user_service_name_);

        return false;
    }

    // 先发起Rpc调用，进行批量用户获取
    chat::UserService_Stub stub(channel.get());
    chat::GetMultiUserInfoReq req;
    chat::GetMultiUserInfoRsp rsp;
    req.set_request_id(Uuid::uuid());
    for (auto& user_id : user_ids) {
        req.add_users_id(user_id.c_str());
    }

    brpc::Controller cntl;
    stub.GetMultiUserInfo(&cntl, &req, &rsp, nullptr);
    if (cntl.Failed()) {
        LOG_ERROR("用户批量信息查询RPC调用失败");

        return false;
    }
    if (!rsp.success()) {
        LOG_ERROR("用户批量信息查询响应错误");

        return false;
    }

    for (auto& user_info : rsp.users_info()) {
        user_infos->emplace(user_info.first, user_info.second);
    }

    return true;
}

bool FriendServiceImpl::getRecentOneMessage(const std::string& chat_session_id,
                                            MessageInfo* message) {
    auto channel = service_manager_->choose(message_service_name_);
    if (!channel) {
        LOG_ERROR("获取消息存储通信信道失败！");
        return false;
    }

    chat::MsgStorageService_Stub stub(channel.get());
    chat::GetRecentMsgReq req;
    chat::GetRecentMsgRsp rsp;

    req.set_request_id(chat::Uuid::uuid());
    req.set_chat_session_id(chat_session_id);
    req.set_msg_count(1);

    brpc::Controller cntl;
    stub.GetRecentMsg(&cntl, &req, &rsp, nullptr);
    if (cntl.Failed()) {
        LOG_ERROR("获取最近消息RPC调用失败");
        return false;
    }
    if (!rsp.success()) {
        LOG_ERROR("获取最近消息响应错误");
        return false;
    }

    if (rsp.msg_list_size()) {
        *message = rsp.msg_list(0);
        return true;
    }

    LOG_WARN("{}: 未找到最近一条消息", chat_session_id);

    return false;
}

// -------------------------------------------------------------------

// FriendServerBuilder

void FriendServerBuilder::makeRegisterClient(const std::string& register_name,
                                             const std::string& etcd_host,
                                             const std::string& access_host) {
    register_client_ = std::make_shared<Registry>(etcd_host);

    register_client_->registry(register_name, access_host);
}

void FriendServerBuilder::makeServiceManager(
    const std::string& user_service_name,
    const std::string& message_service_name) {
    service_manager_ = std::make_shared<ServiceManager>();
    service_manager_->declared(user_service_name);     // 添加关心
    service_manager_->declared(message_service_name);  // 添加关心

    user_service_name_ = user_service_name;
    message_service_name_ = message_service_name;
}

void FriendServerBuilder::makeDiscoveryClient(const std::string& etcd_host,
                                              const std::string& base_service) {
    discovery_client_ = std::make_shared<Discovery>(
        etcd_host, base_service,
        std::bind(&ServiceManager::onServiceOnline, service_manager_.get(),
                  std::placeholders::_1, std::placeholders::_2),
        std::bind(&ServiceManager::onServiceOffline, service_manager_.get(),
                  std::placeholders::_1, std::placeholders::_2));
}

void FriendServerBuilder::makeBrpcServer(const uint16_t port,
                                         const int idle_timeout_sec,
                                         const int num_threads) {
    // 检查
    if (es_client_ == nullptr) {
        LOG_ERROR("ES 客户端未初始化");
        abort();
    }
    if (odb_client_ == nullptr) {
        LOG_ERROR("ODB 客户端未初始化");
        abort();
    }
    if (service_manager_ == nullptr) {
        LOG_ERROR("服务管理对象 未初始化");
        abort();
    }

    brpc_server_ = std::make_shared<brpc::Server>();
    FriendServiceImpl* friend_service =
        new FriendServiceImpl(es_client_, odb_client_, service_manager_,
                              user_service_name_, message_service_name_);
    if (-1 ==
        brpc_server_->AddService(friend_service,
                                 brpc::ServiceOwnership::SERVER_OWNS_SERVICE)) {
        LOG_ERROR("RPC添加服务失败");
        abort();
    }

    brpc::ServerOptions options;
    options.idle_timeout_sec = idle_timeout_sec;
    options.num_threads = num_threads;

    if (-1 == brpc_server_->Start(port, &options)) {
        LOG_ERROR("RPC服务启动失败");
        abort();
    }
}

void FriendServerBuilder::makeOdbClient(
    const int max_pool, const std::string& user, const std::string& password,
    const std::string& db, const std::string& ip, const uint16_t port,
    const std::string& charset) {
    odb_client_ = DataBaseFactory::create(max_pool, user, password, db, ip,
                                          port, charset);
}

void FriendServerBuilder::makeEsClient(
    const std::vector<std::string> host_list) {
    es_client_ = ESClientFactory::create(host_list);
}

FriendServerPtr FriendServerBuilder::build() {
    if (register_client_ == nullptr) {
        LOG_FATAL("服务注册客户端未实例化");
        abort();
    }
    if (discovery_client_ == nullptr) {
        LOG_FATAL("服务发现客户端未实例化");
        abort();
    }
    if (brpc_server_ == nullptr) {
        LOG_FATAL("RPC服务未实例化");
        abort();
    }

    return std::make_shared<FriendServer>(register_client_, discovery_client_,
                                          brpc_server_, es_client_,
                                          odb_client_);
}
}  // namespace chat